use std::io::{self, Write};

use crossterm::{cursor, style, terminal, QueueableCommand, Result};

use xxx_editor::Position;

#[derive(Debug)]
pub struct Screen {
  pub width: u16,
  pub height: u16,
  pub stdout: io::Stdout,
}

impl Screen {
  pub fn new() -> Result<Self> {
    let (columns, rows) = terminal::size()?;

    Ok(Self {
      width: columns,
      height: rows,
      stdout: io::stdout(),
    })
  }

  pub fn draw_rows(
    &mut self,
    rows: &[String],
    rowoff: u16,
    coloff: u16,
  ) -> Result<()> {
    const VERSION: &str = env!("CARGO_PKG_VERSION");

    for row in 0..self.height {
      let filerow: usize = (row + rowoff).into();

      if filerow >= rows.len() {
        if row == self.height / 3 && rows.is_empty() {
          let mut welcome =
            format!("XXX editor - version {VERSION}, Ctrl + q -> Exit");
          welcome.truncate(self.width as usize);

          if welcome.len() < self.width as usize {
            let leftmost = ((self.width as usize - welcome.len()) / 2) as u16;
            self
              .stdout
              .queue(cursor::MoveTo(0, row))?
              .queue(style::Print('~'))?
              .queue(cursor::MoveTo(leftmost, row))?
              .queue(style::Print(welcome))?;
          } else {
            self
              .stdout
              .queue(cursor::MoveTo(0, row))?
              .queue(style::Print(welcome))?;
          }
        } else {
          self
            .stdout
            .queue(cursor::MoveTo(0, row))?
            .queue(style::Print('~'))?;
        }
      } else {
        let mut len = rows[filerow].len();

        if len < coloff.into() {
          continue;
        }

        len -= coloff as usize;

        let start = coloff.into();
        let end = start
          + if len >= self.width.into() {
            self.width.into()
          } else {
            len
          };

        self
          .stdout
          .queue(cursor::MoveTo(0, row))?
          .queue(style::Print(rows[filerow][start..end].to_string()))?;
      }
    }

    // let sa = format!(
    //   "({}, {})/({}, {})",
    //   self.width,
    //   self.height,
    //   self.bounds().x,
    //   self.bounds().y
    // );
    // self
    //   .stdout
    //   .queue(cursor::MoveTo(self.width - sa.len() as u16, self.height))?
    //   .queue(style::Print(sa))?;

    Ok(())
  }

  pub fn clear(&mut self) -> Result<()> {
    self
      .stdout
      .queue(terminal::Clear(terminal::ClearType::All))?
      .queue(cursor::MoveTo(0, 0))?;

    Ok(())
  }

  pub fn flush(&mut self) -> Result<()> {
    self.stdout.flush()
  }

  // pub fn cursor_position(&mut self) -> Result<(u16, u16)> {
  //   cursor::position()
  // }

  pub fn move_to(
    &mut self,
    pos: &Position,
    rowoff: u16,
    coloff: u16,
  ) -> Result<()> {
    self
      .stdout
      .queue(cursor::MoveTo(pos.x - coloff, pos.y - rowoff))?;
    Ok(())
  }

  pub fn bounds(&self) -> Position {
    Position {
      x: self.width,
      y: self.height,
    }
  }
}
